﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace NRank.Examples
{
	public static class ExampleMethods
	{
		public static void Hotels()
		{
			var hotels = new[]
			             	{
			             		new Hotel {Name = "Corinthia", Stars = 4, Area = "Karlov"},
			             		new Hotel {Name = "Interkontinental", Stars = 5, Area = "Karlov"},
			             		new Hotel {Name = "Lazarus' tavern", Stars = 1, Area = "Zlicin"},
			             		new Hotel {Name = "Rwanda", Stars = 5, Area = "Zlicin"},
			             	};

			var muzeums = new[]
			              	{
			              		new Muzeum {Name = "National Muzeum", EntranceCost = 100, Area = "Karlov"},
			              		new Muzeum {Name = "Technical Muzeum", EntranceCost = 50, Area = "Zlicin"},
			              	};

			var join = hotels
				.AsRanked(h => h.Stars)
				.NaiveJoin(
					muzeums.AsRanked(m => (double)m.EntranceCost / (-10.0)),
					ResultFunc,
					null,
					(h, m) => h.Area == m.Area);

			Print(join);

			join = hotels.NaiveJoin(muzeums,
									h => h.Stars * 2,
									m => (double)m.EntranceCost / (-10.0),
									ResultFunc,
									null,
									(h, m) => h.Area == m.Area);

			Console.WriteLine();
			Console.WriteLine();
			Print(join);
		}

		public static void RandomSimpleObject()
		{
			var left = Generate(() => new SimpleObject(RandomChar(5), random.Next(100)), 20).OrderByDescending(o => o.Rank).ToList(); //.AsDepthAware();
			var right = Generate(() => new SimpleObject(RandomChar(5), random.Next(100)), 20).OrderByDescending(o => o.Rank).ToList(); //.AsDepthAware();

			var hashRankJoin = left.HashRankJoin(right, o => o.Rank, o => o.Rank,
			                                   o => o.Name, 
											   o => o.Name,
			                                   (o1, o2) => new SimpleObject(o1.Name, o1.Rank + o2.Rank), PullingStrategyTypes.MinimizeThreshold, (r1, r2) => r1 + r2);

			Console.WriteLine("Left items (top):");
			foreach (var o in left.Take(10))
			{
				Console.WriteLine(o);
			}

			Console.WriteLine("\nRight items (top):");
			foreach (var o in right.Take(10))
			{
				Console.WriteLine(o);
			}

			Console.WriteLine();

			int count = 10;
			foreach (var item in hashRankJoin)
			{
				//Console.WriteLine("{0}, left depth: {1}, right depth: {2}; Threshold: {3}", item, left.Depth, right.Depth, hashRankJoin.Threshold);
				//Console.WriteLine("{0}, Threshold: {1}", item, hashRankJoin.Threshold);
				if (--count == 0) break;
			}
		}

		private static readonly Random random = new Random(0);

		public static string RandomChar(int charRange)
		{
			return Convert.ToChar('A' + random.Next(charRange)).ToString();
		}

		public static void RankJoin()
		{
			var a1 = new[] {5, 4, 3, 2, 1};
			var a2 = new[] {5, 3, 1};

			Print(a1.RankJoin(a2, i => i, j => j, (i, j) => i, PullingStrategyTypes.Alternating, joinCondition: (i, j) => i == j));
		}

		public static void SimpleObjects()
		{
			var left = new SimpleObject[]
			           	{
			           		new SimpleObject("1", 100),
			           		new SimpleObject("2", 50),
			           		new SimpleObject("2", 25),
			           		new SimpleObject("3", 10),
			           	}.AsDepthAware();

			var right = new SimpleObject[]
			            	{
			            		new SimpleObject("3", 5),
			            		new SimpleObject("1", 4),
			            		new SimpleObject("2", 3),
			            		new SimpleObject("2", 2),
			            	}.AsDepthAware();

			var hashRankJoin = left.HashRankJoin(
				right,
				o => o.Rank,
				o => o.Rank,
				o => o.Name,
				o => o.Name,
				(o1, o2) => new SimpleObject(o1.Name, o1.Rank + o2.Rank), PullingStrategyTypes.MinimizeThreshold, (r1, r2) => r1 + r2);

			foreach (var item in hashRankJoin)
			{
				Console.WriteLine("{0}, left depth: {1}, right depth: {2};", item, left.Depth, right.Depth);
			}

			//Print(hashRankJoin);
		}

		public static void DepthAwareTest()
		{
			var chars = new[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }.AsDepthAware();

			foreach (var item in chars)
			{
				Console.WriteLine("Char: {0}, depth: {1}", item, chars.Depth);
			}
		}

		private static void Print<T>(IEnumerable<IRanked<T>> collection)
		{
			foreach (var item in collection)
			{
				Console.WriteLine(item);
			}
		}

		private static Result ResultFunc(Hotel h, Muzeum m)
		{
			return new Result
			       	{
			       		HotelName = h.Name,
			       		MuzeumName = m.Name,
			       		HotelStars = h.Stars,
			       		EntranceCost = m.EntranceCost,
			       	};
		}

		private static void RandomNumbers()
		{
			Random r = new Random(DateTime.Now.Millisecond);
			var c1 = Generate(() => r.Next(1000).AsRanked(), 1000);
			var c2 = Generate(() => r.Next(1000).AsRanked(), 1000);

			Print(c1.NaiveJoin(c2, (i, j) => i + j).Take(20));
		}

		private static IEnumerable<T> Generate<T>(Func<T> creatingFunc, int count)
		{
			while (count-- > 0)
				yield return creatingFunc();
		}
	}
}